package ru.susu.algebra.field.quadratic;

import java.math.BigInteger;

import ru.susu.algebra.operation.IOperation;
import ru.susu.algebra.util.NumberUtilities;

/**
 * @author akargapolov
 * @since: 18.10.2009
 */
public class ExpEvaluateOperation implements IOperation<BigInteger>
{
	public static ExpEvaluateOperation INSTANCE = new ExpEvaluateOperation();

	private static BigInteger ints[] = new BigInteger[10];

	static
	{
		for (int i = 0; i < ints.length; i++)
			ints[i] = BigInteger.valueOf(i);
	}

	/**
	 * На входе целые BigInteger Z, P, K.
	 * @param Z
	 * @param P
	 * @param K
	 */
	@Override
	public BigInteger performOperation(Object... sources)
	{
		BigInteger z = (BigInteger) sources[0];
		BigInteger p = (BigInteger) sources[1];
		BigInteger k = (BigInteger) sources[2];

		int i = NumberUtilities.getFactorPow(z, p);

		// Предложение 2.15 from Aleev
		if (ints[2].equals(p))
		{

			int remainder = k.remainder(ints[8]).intValue();
			if (remainder == 1)
				return ints[2].pow((i <= 2) ? i - 1 : i - 2);
			else if (remainder == 5)
				return ints[3].multiply(ints[2].pow(i - 1));
			else if (remainder == 2 || remainder == 3)
				return ints[2].pow(i);
		}
		else if (!ints[0].equals(k.remainder(p)))
		{
			int x = k.remainder(p).intValue();
			if (QuadraticFieldHelper.isSquareByMod(x, p.intValue()))
				return p.subtract(ints[1]).multiply(p.pow(i - 1));
			else
				return p.pow(2).subtract(ints[1]).multiply(p.pow(i - 1));
		}
		else if (ints[3].equals(p))
		{
			int v = i;
			if (i >= 2 && k.remainder(ints[9]).equals(ints[6]))
				v--;
			return ints[2].multiply(ints[3].pow(v));
		}
		else
			return p.subtract(ints[1]).multiply(p.pow(i));

		throw new RuntimeException("Ahtung! Danger! Case without Proposition 2.15 Z " + z + " P " + p + " K " + k);
	}
}
